package com.campus.schedule.timer;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.campus.common.enums.OrderStatusEnum;
import com.campus.common.enums.PointsRecordSourceEnum;
import com.campus.common.enums.PointsRecordTypeEnum;
import com.campus.schedule.entity.Order;
import com.campus.schedule.entity.PointsRecord;
import com.campus.schedule.entity.User;
import com.campus.schedule.service.OrderService;
import com.campus.schedule.service.PointsRecordService;
import com.campus.schedule.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户积分奖励定时任务
 * 每天凌晨0点10分执行，为完成订单的用户奖励积分
 * 
 * @author ZhaoYuJie
 * @since 2025-01-15
 */
@Slf4j
@Service
public class UserPointsRewardService {

    @Autowired
    private OrderService orderService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private PointsRecordService pointsRecordService;

    /**
     * 每天凌晨0点10分执行积分奖励任务
     * 查询status=4且finishTime早于今天且未奖励积分的订单，为用户奖励积分
     */
    @Scheduled(cron = "0 10 0 * * ?")
    public void rewardUserPoints() {
        log.info("开始执行用户积分奖励任务");
        
        try {
            // 计算今天的开始时间
            LocalDateTime todayStart = LocalDate.now().atStartOfDay();
            
            int totalProcessed = 0;
            int batchSize = 100;
            
            while (true) {
                // 分批查询已完成且未奖励积分的订单，使用limit避免分页查询的数据跳过问题
                LambdaQueryWrapper<Order> orderQuery = new LambdaQueryWrapper<>();
                orderQuery.eq(Order::getStatus, OrderStatusEnum.COMPLETED.getValue()) // 已完成状态
                         .lt(Order::getFinishTime, todayStart) // 完成时间早于今天
                         .eq(Order::getPointsRewarded, 0) // 未奖励积分
                         .isNotNull(Order::getUserId)
                         .isNotNull(Order::getActualPay)
                         .orderByAsc(Order::getFinishTime)
                         .last("LIMIT " + batchSize); // 使用limit限制查询条数
                
                List<Order> orders = orderService.list(orderQuery);
                
                if (orders.isEmpty()) {
                    log.info("没有需要奖励积分的订单，任务结束");
                    break;
                }
                
                log.info("查询到{}个需要奖励积分的订单", orders.size());
                
                // 处理每个订单
                for (Order order : orders) {
                    try {
                        processOrderPointsReward(order);
                        totalProcessed++;
                    } catch (Exception e) {
                        log.error("处理订单{}积分奖励失败: {}", order.getId(), e.getMessage(), e);
                        // 继续处理下一个订单，不中断整个任务
                    }
                }
                
                // 如果查询到的数据少于批次大小，说明已经处理完所有数据
                if (orders.size() < batchSize) {
                    log.info("已处理完所有订单，任务结束");
                    break;
                }
            }
            
            log.info("用户积分奖励任务执行完成，共处理{}个订单", totalProcessed);
            
        } catch (Exception e) {
            log.error("用户积分奖励任务执行失败", e);
            throw e;
        }
    }
    
    /**
     * 处理单个订单的积分奖励
     * 使用独立事务，确保单个订单失败不影响其他订单
     * 
     * @param order 订单信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void processOrderPointsReward(Order order) {
        // 计算奖励积分：每元钱奖励1积分，不足一元则不奖励
        BigDecimal actualPay = order.getActualPay();
        if (actualPay == null || actualPay.compareTo(BigDecimal.ZERO) <= 0) {
            log.warn("订单{}实付金额为空或小于等于0，跳过积分奖励", order.getId());
            return;
        }
        
        int rewardPoints = actualPay.intValue(); // 取整数部分作为奖励积分
        
        if (rewardPoints <= 0) {
            log.info("订单{}实付金额{}不足1元，无积分奖励", order.getId(), actualPay);
            // 即使没有积分奖励，也要标记为已处理，避免重复处理
            markOrderPointsRewarded(order.getId());
            return;
        }
        
        // 查询用户信息
        User user = userService.getById(order.getUserId());
        if (user == null) {
            log.warn("订单{}对应的用户{}不存在，跳过积分奖励", order.getId(), order.getUserId());
            return;
        }
        
        // 更新用户积分
        Integer currentPoints = user.getPoints() != null ? user.getPoints() : 0;
        int newPoints = currentPoints + rewardPoints;
        
        LambdaUpdateWrapper<User> userUpdate = new LambdaUpdateWrapper<>();
        userUpdate.eq(User::getId, user.getId())
                  .set(User::getPoints, newPoints)
                  .set(User::getUpdateTime, LocalDateTime.now());
        
        boolean userUpdateSuccess = userService.update(userUpdate);
        if (!userUpdateSuccess) {
            log.error("更新用户{}积分失败", user.getId());
            throw new RuntimeException("更新用户积分失败");
        }
        
        // 创建积分记录
        PointsRecord pointsRecord = new PointsRecord();
        pointsRecord.setUserId(user.getId());
        pointsRecord.setUserPhone(user.getPhone());
        pointsRecord.setPoints(rewardPoints);
        pointsRecord.setType(PointsRecordTypeEnum.OBTAIN.getValue()); // 1-获得
        pointsRecord.setSource(PointsRecordSourceEnum.ORDER.getValue()); // 来源：订单
        pointsRecord.setSourceId(order.getId());
        pointsRecord.setDescription("订单完成奖励积分");
        pointsRecord.setCreateTime(LocalDateTime.now());
        
        boolean recordSaveSuccess = pointsRecordService.save(pointsRecord);
        if (!recordSaveSuccess) {
            log.error("保存用户{}积分记录失败", user.getId());
            throw new RuntimeException("保存积分记录失败");
        }
        
        // 标记订单积分已奖励
        markOrderPointsRewarded(order.getId());
        
        log.info("订单{}积分奖励成功：用户{} 奖励{}积分，当前积分{}", 
            order.getId(), user.getId(), rewardPoints, newPoints);
    }
    
    /**
     * 标记订单积分已奖励
     * 
     * @param orderId 订单ID
     */
    private void markOrderPointsRewarded(String orderId) {
        LambdaUpdateWrapper<Order> orderUpdate = new LambdaUpdateWrapper<>();
        orderUpdate.eq(Order::getId, orderId)
                   .set(Order::getPointsRewarded, 1);
        
        boolean updateSuccess = orderService.update(orderUpdate);
        if (!updateSuccess) {
            log.error("标记订单{}积分已奖励失败", orderId);
            throw new RuntimeException("标记订单积分已奖励失败");
        }
    }
}